using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Data;
using System.Reflection;
using System.Threading;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.MSProject;
using Microsoft.Win32;
using PSLib = Microsoft.Office.Project.Server.Library;

namespace DataPopulation
{
    class WinProj
    {
        private Application app = null;

        public WinProj(string serverURL)
        {
            if (serverURL.EndsWith("/"))
            {
                serverURL = serverURL.Substring(0, serverURL.Length - 1);
            }
            string paramList = " /s " + serverURL;
            Process.Start(GetWinProjectLocation(), paramList);

            // Wait for Project Server connection to startup
            if (IsProjectRunning(10000.0))
            {
                app = new ApplicationClass();
                app.Alerts((object)false);
                app.DisplayAlerts = false;

                bool connectedToProjectServer = (app.ActiveProject.ServerURL == serverURL);
                app.FileCloseAll(PjSaveType.pjDoNotSave);
                if (!connectedToProjectServer)
                {
                    throw (new System.Exception("Project Professional could not connect to Project Server"));
                }
            }
            else
            {
                throw (new System.Exception("Project Professional failed to startup."));
            }
        }

        public void Dispose()
        {
            if (app != null)
            {
                app.Quit(PjSaveType.pjDoNotSave);
                app = null;
            }
        }

        public void PopulateProject(string projName, string taskPrefix, int numTasks, int maxDuration, int minDuration, 
                                    int numOfDigits, int maxDepDuration, int minDepDuration, 
                                    int maxNumAssn, int minNumAssn, bool popTaskCFs, bool taskLeafsOnly)
        {
            try
            {
                if (app != null)
                {
                    Open(projName);
                    app.OptionsCalculation(false, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);

                    Random rand = new Random(DateTime.Now.Millisecond);

                    // Add tasks.
                    int taskCount = app.ActiveProject.Tasks.Count;
                    if (taskCount < numTasks)
                    {
                        int taskDuration;
                        string taskName;
                        string digits;
                        int currentTask = taskCount + 1;
                        Task theTask;
                        for (int index = 0; index < (numTasks - taskCount); index++)
                        {
                            taskDuration = rand.Next(minDuration, maxDuration + 1);
                            digits = currentTask.ToString();
                            taskName = taskPrefix + digits.PadLeft(numOfDigits, '0');

                            theTask = app.ActiveProject.Tasks.Add(taskName, currentTask);
                            theTask.Duration = taskDuration * 480;
                            currentTask++;
                        }
                    }

                    // Populate the task CFs, add dependencies and add assignments.
                    int resCount = app.ActiveProject.Resources.Count;
                    Resource res;
                    int resIndex = 1;
                    int numAssignments;
                    Task prevTask = null;
                    int maxDepDur = rand.Next(minDepDuration, maxDepDuration + 1);
                    int currentDepDuration = 0;

                    foreach (Task theTask in app.ActiveProject.Tasks)
                    {
                        if (popTaskCFs) PopulateTaskCFs(theTask, taskLeafsOnly);
                        if (resCount > 0)
                        {
                            // Get random number of assignments.
                            if (theTask.Assignments.Count > 0)
                            {
                                theTask.Duration = (int)theTask.Duration / theTask.Assignments.Count;
                            }
                            foreach (Assignment assn in theTask.Assignments)
                            {
                                assn.Delete();
                            }
                            numAssignments = rand.Next(minNumAssn, maxNumAssn + 1);
                            theTask.Duration = (int)theTask.Duration * numAssignments;
                            for (int assnIndex = 1; assnIndex <= numAssignments; assnIndex++)
                            {
                                res = app.ActiveProject.Resources[resIndex];
                                theTask.Assignments.Add(theTask.ID, res.ID, 1);
                                if (resIndex == resCount) resIndex = 0;
                                resIndex++;
                            }
                        }
                        theTask.Predecessors = "";
                        // Add dependencies.
                        if (prevTask != null && currentDepDuration < maxDepDur)
                        {
                            theTask.LinkPredecessors(prevTask, PjTaskLinkType.pjFinishToStart, Missing.Value);
                            currentDepDuration += (int)theTask.Duration / 480;
                        }
                        else
                        {
                            maxDepDur = rand.Next(minDepDuration, maxDepDuration + 1);
                            currentDepDuration = (int)theTask.Duration / 480;
                            
                        }
                        prevTask = theTask;
                    }

                    app.CalculateProject();
                    app.OptionsCalculation(true, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);
                    Save();
                    Close();
                }
            }
            catch (System.Exception ex)
            {
                throw (new System.Exception("WinProj.AddTasks.  " + ex.Message));
            }
        }

        public bool Open(string projName)
        {
            try
            {
                if (app != null)
                {
                    string currentProject = "<>\\" + projName;
                    app.FileOpen(currentProject, false, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, PjPoolOpen.pjDoNotOpenPool, Missing.Value, Missing.Value, Missing.Value, Missing.Value);
                    return true;
                }
                return false;
            }
            catch (System.Exception ex)
            {
                throw(new System.Exception("WinProj.Open " + ex.Message));
            }
        }

        public void Close()
        {
            try
            {
                if (app != null)
                {
                    app.FileCloseEx(PjSaveType.pjDoNotSave, false, !app.ActiveProject.ReadOnly);
                }
            }
            catch (System.Exception ex)
            {
                throw(new System.Exception("WinProj.Close.  " + ex.Message));
            }
        }

        public bool Save()
        {
            try
            {
                if (app != null)
                {
                    if (app.ActiveProject.ReadOnly == false)
                    {
                        app.FileSave();
                        return true;
                    }
                }
                return false;
            }
            catch (System.Exception ex)
            {
                throw(new System.Exception("WinProj.Save.  " + ex.Message));
            }
        }

        private void PopulateTaskCFs(Task theTask, bool leafOnly)
        {
            foreach (wsCustomFields.CustomFieldDataSet.CustomFieldsRow rowCF in Helper.TaskCustomFields)
            {
                if (!rowCF.IsMD_PROP_FORMULANull()) continue;
                PjField fieldID = app.FieldNameToFieldConstant(rowCF.MD_PROP_NAME, PjFieldType.pjTask);

                switch (rowCF.MD_PROP_TYPE_ENUM)
                {
                    case (byte)PSLib.CustomField.Type.TEXT:
                        if (!rowCF.IsMD_LOOKUP_TABLE_UIDNull())
                        {
                            DataRowView rowLTT = Helper.FindLookupTableTree(rowCF.MD_LOOKUP_TABLE_UID);
                            DataRowView rowLTTLeaf = rowLTT;
                            if (rowLTT != null)
                            {
                                if (Helper.LeafOnly(leafOnly, rowCF))
                                {
                                    while (rowLTT != null)
                                    {
                                        rowLTTLeaf = rowLTT;
                                        rowLTT = Helper.FindChildLookupTableTree((Guid)rowLTT["LT_STRUCT_UID"]);
                                    }
                                }
                                theTask.SetField(fieldID, rowLTTLeaf["LT_VALUE_TEXT"].ToString());
                            }
                        }
                        else
                        {
                            theTask.SetField(fieldID, "Created by Data Population");
                        }
                        break;

                    case (byte)PSLib.CustomField.Type.NUMBER:
                        theTask.SetField(fieldID, "123");
                        break;

                    case (byte)PSLib.CustomField.Type.DATE:
                        theTask.SetField(fieldID, DateTime.Now.ToString("d"));
                        break;

                    case (byte)PSLib.CustomField.Type.DURATION:
                        theTask.SetField(fieldID, "45");
                        break;

                    case (byte)PSLib.CustomField.Type.FLAG:
                        theTask.SetField(fieldID, "Yes");
                        break;

                    case (byte)PSLib.CustomField.Type.COST:
                        theTask.SetField(fieldID, "$125.00");
                        break;

                    default:
                        continue;
                }
            }
        }

        private string GetWinProjectLocation()
        {
            RegistryKey rk = Registry.ClassesRoot.OpenSubKey("MSProject.MPD\\shell\\Open\\command");
            string location = (string)rk.GetValue("");
            rk.Close();
            return location.Substring(1, location.Length - 7);
        }

        private bool IsProjectRunning(double WaitTime)
        {
            int i;
            DateTime currentTime = DateTime.Now;
            DateTime adjustedTime = currentTime.AddSeconds(WaitTime);
            TimeSpan targetTime = new TimeSpan(adjustedTime.Ticks);

            i = targetTime.CompareTo(new TimeSpan(DateTime.Now.Ticks));

            while (i >= 0)
            {
                int hwdMain = FindWindow("JWinproj-WhimperMainClass", null);
                if (hwdMain > 0)
                {
                    int hwdMDI = FindWindowEx(hwdMain, 0, "MDIClient", null);
                    if (hwdMDI > 0)
                    {
                        // Project Server initialization not complete till project shows in window
                        if (FindWindowEx(hwdMDI, 0, "Winproj-DocWindowClass", null) > 0)
                            // Found Window
                            return true;
                        else
                            Thread.Sleep(200);
                    }
                    else
                        Thread.Sleep(200);
                }
                else
                    Thread.Sleep(200);

                i = targetTime.CompareTo(new TimeSpan(DateTime.Now.Ticks));
            }
            // Failed to get window before wait time expired
            return false;

        }
        [DllImport("user32.dll", EntryPoint = "FindWindow")]
        private static extern int FindWindow(string strClassName, string strWindowName);
        [DllImport("User32.dll")]
        private static extern int FindWindowEx(int hwndParent, int hwndChildAfter, string strClassName, string strWindowName);
    }
}
